简单计算器

源代码


const readline = require('readline'); // 引入Node.js的readline模块,用于读取用户输入

// 创建一个readline接口实例,用于从标准输入(stdin)读取数据,并将结果输出到标准输出(stdout)
const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

// 定义运算符的优先级,数字越大优先级越高
const precedence = {
    '+': 1, // 加法优先级为1
    '-': 1, // 减法优先级为1
    '*': 2, // 乘法优先级为2
    '/': 2  // 除法优先级为2
};

// 定义一个函数,用于根据运算符计算两个数的结果
function applyOperator(a, b, operator) {
    // 根据运算符执行相应的运算
    switch (operator) {
        case '+': // 加法
            return a + b;
        case '-': // 减法
            return a - b;
        case '*': // 乘法
            return a * b;
        case '/': // 除法
            // 检查除数是否为0,避免除以0的错误
            if (b === 0) throw new Error("除数不能为零");
            return a / b;
        default: // 如果运算符不合法,抛出错误
            throw new Error(`未知运算符: ${operator}`);
    }
}

// 定义一个函数,用于计算数学表达式的值
function evaluateExpression(expr) {
    // 移除表达式中的所有空格,避免空格干扰解析
    expr = expr.replace(/\s+/g, '');

    // 初始化两个栈:一个用于存储数字,另一个用于存储运算符
    const numbers = [];  // 数字栈
    const operators = []; // 运算符栈

    // 从左到右遍历表达式
    let i = 0;
    while (i < expr.length) {
        // 如果当前字符是数字
        if (/\d/.test(expr[i])) {
            let numStr = ''; // 用于存储当前数字的字符串
            // 继续读取数字,直到遇到非数字字符
            while (i < expr.length && (/\d/.test(expr[i]) || expr[i] === '.')) {
                numStr += expr[i]; // 将当前字符添加到数字字符串中
                i++;
            }
            numbers.push(parseFloat(numStr)); // 将解析出的数字转换为浮点数并压入数字栈
        }
        // 如果当前字符是运算符
        else if (expr[i] in precedence) {
            // 如果运算符栈不为空,并且栈顶运算符的优先级大于或等于当前运算符的优先级
            while (operators.length > 0 &&
            precedence[operators[operators.length - 1]] >= precedence[expr[i]]) {
                // 从数字栈中弹出两个数字
                const b = numbers.pop();
                const a = numbers.pop();
                // 从运算符栈中弹出一个运算符
                const op = operators.pop();
                // 计算结果并压入数字栈
                numbers.push(applyOperator(a, b, op));
            }
            // 将当前运算符压入运算符栈
            operators.push(expr[i]);
            i++;
        } else {
            // 如果遇到非法字符,抛出错误
            throw new Error(`非法字符: ${expr[i]}`);
        }
    }

    // 处理剩余的运算符
    while (operators.length > 0) {
        // 从数字栈中弹出两个数字
        const b = numbers.pop();
        const a = numbers.pop();
        // 从运算符栈中弹出一个运算符
        const op = operators.pop();
        // 计算结果并压入数字栈
        numbers.push(applyOperator(a, b, op));
    }

    // 如果数字栈中只有一个数字,说明表达式计算完成
    if (numbers.length !== 1) {
        throw new Error("表达式不完整");
    }

    // 返回最终结果
    return numbers[0];
}

// 定义主交互函数,用于启动计算器
function startCalculator() {
    // 打印欢迎信息和使用说明
    console.log('=== 简单计算器 ===');
    console.log('支持运算符: +, -, *, /');
    console.log('示例输入: 3 + 5 * 2 / 4');

    // 监听用户输入
    rl.on('line', (input) => {
        // 如果用户输入"exit",退出程序
        if (input.toLowerCase() === 'exit') {
            console.log('感谢使用计算器!');
            rl.close(); // 关闭readline接口
            return;
        }

        try {
            // 尝试计算用户输入的表达式
            const result = evaluateExpression(input);
            // 输出计算结果,保留两位小数
            console.log(`结果: ${result.toFixed(2)}`);
        } catch (error) {
            // 如果计算过程中发生错误,输出错误信息
            console.log(`错误: ${error.message}`);
        }
    });
}

// 启动计算器
startCalculator();
                    

运行结果

> 等待执行...
计算:

代码解释

引入模块和创建接口

通过`require('readline')`引入Node.js的`readline`模块,用于读取用户输入。然后创建`rl`实例,设置输入为标准输入`process.stdin`,输出为标准输出`process.stdout`,实现与用户的交互。

定义运算符优先级

使用对象`precedence`定义了四则运算符`+`、`-`、`*`、`/`的优先级,乘法和除法的优先级为2,加法和减法的优先级为1,数字越大优先级越高。

计算函数`applyOperator`

`applyOperator`函数根据传入的运算符,对两个数`a`和`b`执行相应的运算。在除法运算中,检查除数是否为0,如果为0则抛出错误,其他不合法的运算符也会抛出错误。

表达式求值函数`evaluateExpression`

该函数先移除表达式中的空格,然后初始化数字栈`numbers`和运算符栈`operators`。通过遍历表达式,将数字压入数字栈,根据运算符优先级处理运算符,最后处理剩余运算符,确保表达式按正确顺序计算,计算完成后返回结果。如果数字栈中最终不是只有一个数字,说明表达式不完整,会抛出错误。

主交互函数`startCalculator`

此函数打印欢迎信息和使用说明,然后监听用户输入。如果用户输入`exit`,关闭`readline`接口退出程序;否则尝试计算用户输入的表达式,输出计算结果或错误信息。